import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;

import javax.swing.JOptionPane;


public class PhysicsMethods{

	Rectangle r2;
	Point p1;
	Point p3;
	Point vvec; //velocity vector



	//Return [d(x), d(y), new vx, new vy, [1:if collision occurred, 0 if no collision occurred]]
	//i.e. the displacement of the rectangle, and the new velocities.
	public static double[] collisionStuffPro(Rectangle r2, Draw draw, Point2D.Double vvec ){

		int pvhb = 0; // lol: "PerfectVerticalorHorizontalBounce" = PVHB
		//1 = bounce off of perfectly horizontal
		//2 = bounce off of perfectly vertical
		
		Point p1=(Point) draw.start().clone(), p3 = (Point) draw.end().clone();
		Point p2 = findPoint2(p1.x, p1.y, p3.x, p3.y, (int)r2.getCenterX(), (int)r2.getCenterY());

		if( p1.y == p3.y){ //horiz
			if( p2.equals(p1)){
				p1.y +=250;
			}else if ( p2.equals(p3)){
				p3.y -=250;
			}
			pvhb = 1;
		}else if(p1.x == p3.x){
			if( p2.equals(p1)){
				p1.x += 250;
			}else if ( p2.equals(p3)){
				p3.x -= 250;
			}		
			pvhb = 2;
		}


		Point n1vec = new Point((p3.y - p1.y),p1.x - p3.x); //Normal vector

		double proj_a = (n1vec.x*(p2.x - p1.x) + n1vec.y*(p2.y-p1.y))/n1vec.distance(0,0);  //generalized projection for p2 = angle opposite hypot

		Point p1cr_vec = new Point((int)r2.getCenterX()-p1.x, (int)r2.getCenterY() - p1.y);

		double rproj = project(p1cr_vec,n1vec);
		
		//Project the p1-center vector onto N1 to find the center of the shape...
		double s_radius = Math.abs(project(new Point(0,r2.height/2),n1vec)) + Math.abs(project(new Point(r2.width/2,0),n1vec));
		
		//If overlap is detected
		if(		overlap( p1.y, p3.y, r2.getMinY(), r2.getMaxY()) &&
				overlap( p3.x,p1.x,r2.getMinX(),r2.getMaxX())&& 
				overlap(rproj-s_radius,rproj+s_radius,0,proj_a)	){//then...

			//now that we know a collision takes place, we don't feel bad about about allocationg double arrays...
			int dispindex=-1;
			double [] dispvec = new double[]{
					overlap2(0, proj_a,rproj-s_radius, rproj+s_radius),
					overlap2( p3.x, p1.x, r2.getMinX(), r2.getMaxX() ),
					overlap2( p1.y, p3.y, r2.getMinY(), r2.getMaxY())};

			double[] mdv=new double[]{Math.abs(dispvec[0]), Math.abs(dispvec[1]), Math.abs(dispvec[2])};
			if(mdv[0]< mdv[1]){
				dispindex = (mdv[0]< mdv[2])? 0:2;
			}else{
				dispindex = (mdv[1]< mdv[2])? 1:2;
			}

			//TODO: only calculate this if the dispind shows the reflection was hypotenal? horiz and vert. imapacts just reflect over xyaxis
			//Calculate resulting velocity vector from incident velocity vector
			// I' = I - 2*dot(I,Nhat)*Nhat
			//aka I' = I - 2 proj(I onto Nhat)*nhat
			double theta = Math.atan2( n1vec.y, n1vec.x);

			Point2D.Double I = null;
			
			if( pvhb == 0 )	{
				double magn1 = n1vec.distance(0,0);
				double nx = n1vec.x / magn1,ny = n1vec.y / magn1;
				double P = vvec.x * nx + vvec.y * ny;

				I = new Point2D.Double( vvec.x-2*P*nx , vvec.y-2*P*ny);
				
			}else if( pvhb == 1){ // bounce perfectly up/down
				I = new Point2D.Double( vvec.x , -vvec.y);
			}else if( pvhb == 2){ // bounce perfectly L/R
				I = new Point2D.Double( -vvec.x, vvec.y);
			}else{
				System.err.println("okay wth. none of the pvhbs showed up o.o");
				System.err.println("so yeah ima ragequit now kthxbai.");
				System.exit(0);
			}




			//scale velocity properly
			double sfactor = I.distance(0, 0);
			I.x *= scalefnx(draw.length())/sfactor;
			I.y *= scalefnx(draw.length())/sfactor ;
			
			

			if(dispindex == 0){

//				System.out.println("AS;LF JAS;DFKLJ AS;DFKLJAS;DFKLJ AS;F");
				//calc theta now that we have a use for it...
				return new double[]{dispvec[0]*Math.cos(theta), dispvec[0]*Math.sin(theta), I.x /*dvx*/,I.y  /*dvy*/ , 1};
			}else if (dispindex == 1){
//				System.out.println("bS;LF JAS;DFKLJ AS;DFKLJAS;DFKLJ AS;F");
				return new double[]{dispvec[1],0, I.x /*v'x*/,I.y  /*v'y*/ , 1};
			}else if (dispindex == 2){
//				System.out.println("cS;LF JAS;DFKLJ AS;DFKLJAS;DFKLJ AS;F");
				return new double[]{0, dispvec[2], I.x /*v'x*/,I.y  /*v'y*/, 1 };
			}

		}

		//no collision so no change in anythingness....
		//System.out.println("null 0'd"); // <--- wth does this mean again? xP
		return new double[]{0,0,0,0,0};

	}

	public static double[] collisionStuffNoob(Rectangle r2, Draw draw, Point2D.Double vvec ){

		Point p1=draw.start(), p3 = draw.end();


		Point p2 = findPoint2(p1.x, p1.y, p3.x, p3.y, (int)r2.getCenterX(), (int)r2.getCenterY());

		Point n1vec = new Point((p3.y - p1.y),p1.x - p3.x); //Normal vector

		double proj_a = (n1vec.x*(p2.x - p1.x) + n1vec.y*(p2.y-p1.y))/n1vec.distance(0,0);  //generalized projection for p2 = angle opposite hypot

		Point p1cr_vec = new Point((int)r2.getCenterX()-p1.x, (int)r2.getCenterY() - p1.y);

		double rproj = project(p1cr_vec,n1vec);

		//Project the p1-center vector onto N1 to find the center of the shape...
		double s_radius = Math.abs(project(new Point(0,r2.height/2),n1vec)) + Math.abs(project(new Point(r2.width/2,0),n1vec));

		//If overlap is detected
		if(		overlap( p1.y, p3.y, r2.getMinY(), r2.getMaxY()) &&
				overlap( p3.x,p1.x,r2.getMinX(),r2.getMaxX())&& 
				overlap(rproj-s_radius,rproj+s_radius,0,proj_a)	){//then...

			//now that we know a collision takes place, we don't feel bad about about allocationg double arrays...
			int dispindex=-1;
			double [] dispvec = new double[]{
					overlap2(0, proj_a,rproj-s_radius, rproj+s_radius),
					overlap2( p3.x, p1.x, r2.getMinX(), r2.getMaxX() ),
					overlap2( p1.y, p3.y, r2.getMinY(), r2.getMaxY())};



			double[] mdv=new double[]{Math.abs(dispvec[0]), Math.abs(dispvec[1]), Math.abs(dispvec[2])};
			if(mdv[0]< mdv[1]){
				dispindex = (mdv[0]< mdv[2])? 0:2;
			}else{
				dispindex = (mdv[1]< mdv[2])? 1:2;
			}

			//TODO: only calculate this if the dispind shows the reflection was hypotenal? horiz and vert. imapacts just reflect over xyaxis
			//Calculate resulting velocity vector from incident velocity vector
			// I' = I - 2*dot(I,Nhat)*Nhat
			//aka I' = I - 2 proj(I onto Nhat)*nhat



			double P = project(vvec.x, vvec.y, n1vec.x, n1vec.y);///n1vec.distance(0, 0);

			Point2D.Double I = new Point2D.Double(
					(vvec.x - 2* P* n1vec.x/n1vec.distance(0, 0)),
					(vvec.y - 2* P* n1vec.y/n1vec.distance(0, 0))
			);		

			//			
			//			double magn1 = n1vec.distance(0,0);
			//			double nx = n1vec.x / magn1,ny = n1vec.y / magn1;
			//			double P = vvec.x * nx + vvec.y * ny;
			//						
			//			Point2D.Double I = new Point2D.Double( vvec.x-2*P*nx , vvec.y-2*P*ny);
			//						
			//			double sfactor = I.distance(0, 0);
			//		
			//			
			//			I.x *= scalefnx(draw.length())/sfactor;
			//			I.y *= scalefnx(draw.length())/sfactor ;			





			if(dispindex == 0){
				double theta = Math.atan2( n1vec.y, n1vec.x);
				//calc theta now that we have a use for it...
				return new double[]{dispvec[0]*Math.cos(theta), dispvec[0]*Math.sin(theta), I.x /*dvx*/,I.y  /*dvy*/ , 1};
			}else if (dispindex == 1){
				return new double[]{dispvec[1],0, I.x /*v'x*/,I.y  /*v'y*/ , 1};
			}else if (dispindex == 2){
				return new double[]{0, dispvec[2], I.x /*v'x*/,I.y  /*v'y*/, 1 };
			}

		}

		//no collision so no change in anythingness....
		//System.out.println("null 0'd"); // <--- wth does this mean again? xP
		return new double[]{0,0,0,0,0};

	}

	public static double scalefnx(double len){
		return len/100*15;
	}

	//Does an overlap exist?
	public static boolean overlap(double a1, double a2, double b1, double b2){

		double r_ep = Math.max(Math.max(a1,a2),Math.max(b1,b2));
		double l_ep = Math.min(Math.min(a1,a2),Math.min(b1,b2));

		double overlap = Math.abs(r_ep - l_ep) - Math.abs( a2-a1) - Math.abs(b2-b1);

		if(overlap < 0) return true;
		return false;

	}

	//Find point 2 based on points 1 and 3... (hypotenal points)
	public static Point findPoint2(int ax,int ay,int bx,int by,int qx,int qy){
		Point pt = new Point();
		//bx should always be the right-most point.
		if(bx < ax){
			//swap p3 and p1...
			//System.out.println("swapperz");
			int tx = ax, ty = ay;
			ax = bx; ay = by;
			bx = tx; by = ty;
		}

		double slope = (double)(by-ay)/ (double)(bx-ax);
		if( qy - ay > slope*( qx - ax)){ //above
			pt.y = Math.min(ay, by);

			if( slope>=0){
				pt.x = bx;				
			}else{
				pt.x = ax;

			}
		}else{
			pt.y = Math.max(ay,by);

			if(slope >=0){
				pt.x = ax;
			}else{
				pt.x = bx;
			}
		}

		return pt;

		//TODO: Watch for the case where a and b are points that are right above one-another (divide by 0 error in the slope).

	}

	//Determines the displacement for the movable-object (namely, the second one)
	//The immovable object's projection needs to be specified first!!!!!! (as a1,a2)
	public static double overlap2(double a1, double a2, double b1, double b2){

		if(a1>a2){
			double t = a1;
			a1 = a2;
			a2 = t;			
		}
		if(b1>b2){
			double t = b1;
			b1 = b2;
			b2 = t;			
		}

		if(  Math.abs(a1-b2) > Math.abs(a2-b1)) //displace to the right
			return Math.abs(a2-b1);
		//otherwise:
		return -Math.abs(a1-b2);

	}


	//Vector projection (v1 onto v2)
	public static double project(Point v1,/*onto*/ Point v2){
		double a = v1.x*v2.x + v1.y*v2.y;
		return a/v2.distance(0, 0);
	}
	public static double project(double v1x, double v1y, /*onto*/ double v2x, double v2y){
		double a = v1x*v2x + v1y*v2y;
		return a/Math.sqrt( v1x*v1x + v1y*v1y  );
	}



	//	public void drawLine(Graphics2D g2, double x, double y, double x1, double y1){
	//		g2.drawLine( (int)Math.round(x), (int)Math.round(y), (int)Math.round(x1), (int)Math.round(y1));
	//	}





	//	public static void main(String[]args){
	//		new nphys_method();		
	//	}
	//	public void mouseDragged(MouseEvent arg0) {
	//	}
	//	public void mouseMoved(MouseEvent arg0) {
	//		r2.setLocation((int)(arg0.getPoint().x - r2.getWidth()/2.0),(int)( arg0.getPoint().y - r2.getHeight()/2.0));
	//	}


	//	public nphys_method(){
	//		setTitle("N-PhysTutorial test");
	//		setSize(400,600);
	//		setVisible(true);
	//		setDefaultCloseOperation(EXIT_ON_CLOSE);
	//
	//		(new Timer(10, new ActionListener() {
	//			public void actionPerformed(ActionEvent e) {
	//				repaint();				
	//			}
	//		})).start();
	//		this.addMouseMotionListener(this);
	//
	//	}
}
